package com.ruoyi.knowledge.controller;

import java.util.List;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.knowledge.domain.KnowledgeFile;
import com.ruoyi.knowledge.domain.KnowledgeFileSegment;
import com.ruoyi.knowledge.service.IKnowledgeFileSegmentService;
import com.ruoyi.knowledge.service.IKnowledgeFileService;
import com.ruoyi.chat.utils.ChatLangchainUtils;
import com.ruoyi.knowledge.constant.KnowledgeConstants;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.store.embedding.EmbeddingStore;

/**
 * 知识库文件切片Controller
 *
 * @author ruoyi
 */
@Tag(name = "知识库文件切片管理")
@RestController
@RequestMapping("/knowledge-base/file")
public class KnowledgeFileSegmentController extends BaseController {
    private static final Logger log = LoggerFactory.getLogger(KnowledgeFileSegmentController.class);

    @Autowired
    private IKnowledgeFileSegmentService knowledgeFileSegmentService;

    @Autowired
    private IKnowledgeFileService knowledgeFileService;

    @Autowired
    private ChatLangchainUtils chatLangchainUtils;

    /**
     * 查询文件切片列表
     */
    @Operation(
            summary = "查询文件切片列表",
            parameters = {
                    @Parameter(name = "fileId", description = "文件ID")
            })
    @GetMapping("/{fileId}/segments")
    public TableDataInfo listSegments(@PathVariable("fileId") Long fileId) {
        startPage();

        log.info("查询文件切片列表, 文件ID: {}", fileId);

        // 验证文件是否存在
        KnowledgeFile file = knowledgeFileService.selectKnowledgeFileById(fileId);
        if (file == null) {
            log.warn("文件不存在, ID: {}", fileId);
            return getDataTable(java.util.Collections.emptyList());
        }

        // 查询切片列表
        KnowledgeFileSegment query = new KnowledgeFileSegment();
        query.setFileId(fileId);
        List<KnowledgeFileSegment> list = knowledgeFileSegmentService.selectKnowledgeFileSegmentList(query);

        log.info("文件切片查询完成, 文件ID: {}, 切片数量: {}", fileId, list.size());

        return getDataTable(list);
    }

    /**
     * 获取文件切片详细信息
     */
    @Operation(
            summary = "获取文件切片详细信息",
            parameters = {
                    @Parameter(name = "id", description = "切片ID")
            })
    @GetMapping("/segment/{id}")
    public AjaxResult getSegmentInfo(@PathVariable("id") Long id) {
        log.info("查询切片详情, 切片ID: {}", id);
        return success(knowledgeFileSegmentService.selectKnowledgeFileSegmentById(id));
    }

    /**
     * 新增文件切片
     */
    @Operation(
            summary = "新增文件切片",
            parameters = {
                    @Parameter(name = "knowledgeFileSegment", description = "文件切片信息")
            })
    @PostMapping("/segment")
    @PreAuthorize("@ss.hasPermi('knowledge:file:edit')")
    @Log(title = "知识库文件切片", businessType = BusinessType.INSERT)
    public AjaxResult add(@RequestBody KnowledgeFileSegment knowledgeFileSegment) {
        log.info("新增切片, 文件ID: {}", knowledgeFileSegment.getFileId());

        // 验证文件是否存在
        KnowledgeFile file = knowledgeFileService.selectKnowledgeFileById(knowledgeFileSegment.getFileId());
        if (file == null) {
            return error("文件不存在");
        }

        // 检查序号是否唯一
        KnowledgeFileSegment checkSegment = new KnowledgeFileSegment();
        checkSegment.setFileId(knowledgeFileSegment.getFileId());
        checkSegment.setSegmentIndex(knowledgeFileSegment.getSegmentIndex());
        List<KnowledgeFileSegment> list = knowledgeFileSegmentService.selectKnowledgeFileSegmentList(checkSegment);
        if (list != null && !list.isEmpty()) {
            return error("该文件下已存在相同序号的切片");
        }

        // 新增切片
        int rows = knowledgeFileSegmentService.insertKnowledgeFileSegment(knowledgeFileSegment);

        // 更新文件向量数量
        if (rows > 0) {
            updateFileVectorCount(knowledgeFileSegment.getFileId());

            // 更新Redis向量库
            if (knowledgeFileSegment.getContent() != null && !knowledgeFileSegment.getContent().isEmpty()) {
                try {
                    // 如果前端未提供向量ID，生成一个新的
                    if (knowledgeFileSegment.getVectorId() == null || knowledgeFileSegment.getVectorId().isEmpty()) {
                        knowledgeFileSegment.setVectorId("vector:" + knowledgeFileSegment.getFileId() + ":" + knowledgeFileSegment.getSegmentIndex());
                        // 更新数据库中的向量ID
                        knowledgeFileSegmentService.updateKnowledgeFileSegment(knowledgeFileSegment);
                    }

                    // 将切片内容添加到向量库
                    updateVectorStore(knowledgeFileSegment, file.getKnowledgeId());
                } catch (Exception e) {
                    log.error("更新Redis向量库失败", e);
                    // 不影响主流程，返回成功
                }
            }
        }

        return toAjax(rows);
    }

    /**
     * 修改文件切片
     */
    @Operation(
            summary = "修改文件切片",
            parameters = {
                    @Parameter(name = "id", description = "切片ID"),
                    @Parameter()
            }
    )
    @PutMapping("/segment/{id}")
    @PreAuthorize("@ss.hasPermi('knowledge:file:edit')")
    @Log(title = "知识库文件切片", businessType = BusinessType.UPDATE)
    public AjaxResult edit(@PathVariable("id") Long id, @RequestBody KnowledgeFileSegment knowledgeFileSegment) {
        log.info("修改切片, 切片ID: {}", id);

        // 设置ID
        knowledgeFileSegment.setId(id);

        // 获取原切片信息
        KnowledgeFileSegment originalSegment = knowledgeFileSegmentService.selectKnowledgeFileSegmentById(id);
        if (originalSegment == null) {
            return error("切片不存在");
        }

        // 更新切片
        int rows = knowledgeFileSegmentService.updateKnowledgeFileSegment(knowledgeFileSegment);

        // 如果更新成功，且内容有变化，更新向量库
        if (rows > 0 && !originalSegment.getContent().equals(knowledgeFileSegment.getContent())) {
            // 获取文件信息，用于获取知识库ID
            KnowledgeFile file = knowledgeFileService.selectKnowledgeFileById(originalSegment.getFileId());
            if (file != null) {
                try {
                    // 更新向量库
                    updateVectorStore(knowledgeFileSegment, file.getKnowledgeId());
                } catch (Exception e) {
                    log.error("更新Redis向量库失败", e);
                    // 不影响主流程，返回成功
                }
            }
        }

        return toAjax(rows);
    }

    /**
     * 删除文件切片
     */
    @Operation(
            summary = "删除文件切片",
            parameters = {
                    @Parameter(name = "id", description = "切片ID")
            })
    @DeleteMapping("/segment/{id}")
    @PreAuthorize("@ss.hasPermi('knowledge:file:remove')")
    @Log(title = "知识库文件切片", businessType = BusinessType.DELETE)
    public AjaxResult remove(@PathVariable Long id) {
        log.info("删除切片, 切片ID: {}", id);

        // 获取原切片信息，用于后续更新文件向量数量
        KnowledgeFileSegment segment = knowledgeFileSegmentService.selectKnowledgeFileSegmentById(id);
        if (segment == null) {
            return error("切片不存在");
        }

        Long fileId = segment.getFileId();

        // 如果有向量ID，尝试从Redis向量库中删除
        if (segment.getVectorId() != null && !segment.getVectorId().isEmpty()) {
            // 获取文件信息，用于获取知识库ID
            KnowledgeFile file = knowledgeFileService.selectKnowledgeFileById(segment.getFileId());
            if (file != null) {
                try {
                    // 从向量库中删除
                    removeFromVectorStore(segment.getVectorId(), file.getKnowledgeId());
                } catch (Exception e) {
                    log.error("从Redis向量库删除失败", e);
                    // 不影响主流程，继续执行
                }
            }
        }

        // 删除切片
        int rows = knowledgeFileSegmentService.deleteKnowledgeFileSegmentById(id);

        // 更新文件向量数量
        if (rows > 0 && fileId != null) {
            updateFileVectorCount(fileId);
        }

        return toAjax(rows);
    }

    /**
     * 批量删除文件切片
     */
    @Operation(
            summary = "批量删除文件切片",
            parameters = {
                    @Parameter(name = "ids", description = "切片ID列表")
            })
    @DeleteMapping("/segment/batch")
    @PreAuthorize("@ss.hasPermi('knowledge:file:remove')")
    @Log(title = "知识库文件切片", businessType = BusinessType.DELETE)
    public AjaxResult batchRemove(@RequestBody Long[] ids) {
        log.info("批量删除切片, 数量: {}", ids.length);

        // 获取第一个切片的文件ID，用于后续更新文件向量数量
        Long fileId = null;
        if (ids.length > 0) {
            KnowledgeFileSegment segment = knowledgeFileSegmentService.selectKnowledgeFileSegmentById(ids[0]);
            if (segment != null) {
                fileId = segment.getFileId();
            }
        }

        // 批量删除切片
        int rows = knowledgeFileSegmentService.deleteKnowledgeFileSegmentByIds(ids);

        // 更新文件向量数量
        if (rows > 0 && fileId != null) {
            updateFileVectorCount(fileId);
        }

        return toAjax(rows);
    }

    /**
     * 更新文件的向量数量
     */
    @Operation(
            summary = "更新文件的向量数量",
            parameters = {
                    @Parameter(name = "fileId", description = "文件ID")
            })
    private void updateFileVectorCount(Long fileId) {
        try {
            // 查询所有切片计算向量数量
            KnowledgeFileSegment query = new KnowledgeFileSegment();
            query.setFileId(fileId);
            List<KnowledgeFileSegment> segments = knowledgeFileSegmentService.selectKnowledgeFileSegmentList(query);

            // 统计有效向量数量（向量ID不为空）
            long vectorCount = segments.stream()
                    .filter(s -> s.getVectorId() != null && !s.getVectorId().isEmpty())
                    .count();

            // 更新文件向量数量
            KnowledgeFile file = knowledgeFileService.selectKnowledgeFileById(fileId);
            if (file != null) {
                file.setVectorCount((int) vectorCount);
                knowledgeFileService.updateKnowledgeFile(file);

                log.info("更新文件向量数量, 文件ID: {}, 向量数量: {}", fileId, vectorCount);
            }
        } catch (Exception e) {
            log.error("更新文件向量数量失败", e);
        }
    }

    /**
     * 更新Redis向量库
     *
     * @param segment 切片对象
     * @param knowledgeId 知识库ID
     */
    @Operation(
            summary = "更新Redis向量库",
            parameters = {
                    @Parameter(name = "segment", description = "切片对象"),
                    @Parameter(name = "knowledgeId", description = "知识库ID")
            })
    private void updateVectorStore(KnowledgeFileSegment segment, Long knowledgeId) {
        try {
            if (segment.getContent() == null || segment.getContent().isEmpty()) {
                log.warn("切片内容为空，跳过向量库更新, 切片ID: {}", segment.getId());
                return;
            }

            // 创建命名空间
            String namespace = KnowledgeConstants.VECTOR_NAMESPACE_PREFIX + knowledgeId;
            log.info("更新向量库, 命名空间: {}, 切片ID: {}", namespace, segment.getId());

            // 获取向量库和Embedding模型
            EmbeddingStore<TextSegment> embeddingStore = chatLangchainUtils.getLocalEmbeddedStore(namespace);

            // 将文本转为TextSegment
            TextSegment textSegment = TextSegment.from(segment.getContent());

            // 生成向量
            Embedding embedding = chatLangchainUtils.getEmbeddingModel().embed(segment.getContent()).content();

            // 确保向量ID不为空
            String vectorId = segment.getVectorId();
            if (vectorId == null || vectorId.isEmpty()) {
                vectorId = "vector:" + segment.getFileId() + ":" + segment.getSegmentIndex();
                // 更新数据库
                segment.setVectorId(vectorId);
                knowledgeFileSegmentService.updateKnowledgeFileSegment(segment);
            }

            // 添加到向量库
            embeddingStore.add(embedding, textSegment);

            log.info("向量库更新成功, 切片ID: {}, 向量ID: {}", segment.getId(), vectorId);
        } catch (Exception e) {
            log.error("更新向量库失败", e);
            throw e;
        }
    }

    /**
     * 从Redis向量库中删除向量
     *
     * @param vectorId 向量ID
     * @param knowledgeId 知识库ID
     */
    @Operation(
            summary = "从Redis向量库中删除向量",
            parameters = {
                    @Parameter(name = "vectorId", description = "向量ID"),
                    @Parameter()
            }
    )
    private void removeFromVectorStore(String vectorId, Long knowledgeId) {
        try {
            // 创建命名空间
            String namespace = KnowledgeConstants.VECTOR_NAMESPACE_PREFIX + knowledgeId;
            log.info("从向量库删除向量, 命名空间: {}, 向量ID: {}", namespace, vectorId);

            // 这里需要根据实际的Redis向量库删除API进行调用
            // 由于Langchain4j库的限制，当前无法直接通过ID删除向量
            // 此处留作扩展，实际项目中可以通过直接操作Redis来实现
            log.warn("当前版本不支持直接删除向量, 向量ID: {}", vectorId);
        } catch (Exception e) {
            log.error("从向量库删除向量失败", e);
            throw e;
        }
    }
}
